﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Controls.Primitives;
using System.Reflection;
using System.Windows.Data;

namespace EasySL.Controls
{
    public partial class ExDataGrid : DataGrid
    { 
        #region PageSize
        /// <summary>
        /// Identifies the PageSize dependency property.
        /// </summary>
        public static readonly DependencyProperty PageSizeProperty =
            DependencyProperty.Register("PageSize",
                typeof(int),
                typeof(ExDataGrid),
                new PropertyMetadata(10)
                );

        /// <summary>
        /// Gets or sets PageSize.
        /// </summary>
        public int PageSize
        {
            get { return (int)GetValue(PageSizeProperty); }
            set { SetValue(PageSizeProperty, value); }
        }
        #endregion 

        #region SortProperty
        /// <summary>
        /// Identifies the SortProperty dependency property, which decides default order by property.
        /// </summary>
        public static readonly DependencyProperty SortPropertyProperty = 
            DependencyProperty.Register("SortProperty", 
                typeof(string), 
                typeof(ExDataGrid), 
                null);

        /// <summary>
        /// Gets or sets SortProperty.
        /// </summary>
        public string SortProperty
        {
            get { return (string)GetValue(SortPropertyProperty); }
            set { SetValue(SortPropertyProperty, value); }
        }
        #endregion

        #region GroupProperty
        /// <summary>
        /// Identifies the GroupProperty dependency property.
        /// </summary>
        public static readonly DependencyProperty GroupPropertyProperty =
            DependencyProperty.Register("GroupProperty",
                typeof(string),
                typeof(ExDataGrid),
                null);

        /// <summary>
        /// Gets or sets GroupProperty.
        /// </summary>
        public string GroupProperty
        {
            get { return (string)GetValue(GroupPropertyProperty); }
            set { SetValue(GroupPropertyProperty, value); }
        }
        #endregion

        #region IsAscend
        /// <summary>
        /// Identifies the IsAscend dependency property, which decides default order sequence.
        /// </summary>
        public static readonly DependencyProperty IsAscendProperty =
            DependencyProperty.Register("IsAscend",
                typeof(bool),
                typeof(ExDataGrid),
                null);

        /// <summary>
        /// Gets or sets IsAscend.
        /// </summary>
        public bool IsAscend
        {
            get { return (bool)GetValue(IsAscendProperty); }
            set { SetValue(IsAscendProperty, value); }
        }
        #endregion

        #region Desc
        /// <summary>
        /// Identifies the Description dependency property.
        /// </summary>
        public static readonly DependencyProperty DescProperty =
            DependencyProperty.Register("Desc",
                typeof(object),
                typeof(ExDataGrid),
                null);

        /// <summary>
        /// Gets or sets Desc.
        /// </summary>
        public object Desc
        {
            get { return (object)GetValue(DescProperty); }
            set { SetValue(DescProperty, value); }
        }
        #endregion  

        #region IsShowSummaryRow
        public bool IsShowSummaryRow
        {
            get { return (bool)GetValue(IsShowSummaryRowProperty); }
            set { SetValue(IsShowSummaryRowProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsShowSummaryRow.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsShowSummaryRowProperty =
            DependencyProperty.Register("IsShowSummaryRow", typeof(bool), typeof(ExDataGrid), new PropertyMetadata(false));
        #endregion

        #region IsShowRowNumber
        public bool IsShowRowNumber
        {
            get { return (bool)GetValue(IsShowRowNumberProperty); }
            set { SetValue(IsShowRowNumberProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsShowRowNumber.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsShowRowNumberProperty =
            DependencyProperty.Register("IsShowRowNumber", typeof(bool), typeof(ExDataGrid), new PropertyMetadata(true));
        #endregion

        #region EnableVerticalScroll
        public bool EnableVerticalScroll
        {
            get { return (bool)GetValue(EnableVerticalScrollProperty); }
            set { SetValue(EnableVerticalScrollProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EnableVerticalScroll.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EnableVerticalScrollProperty =
            DependencyProperty.Register("EnableVerticalScroll", typeof(bool), typeof(ExDataGrid), new PropertyMetadata(true));
        #endregion

        #region EnableCheckAll
        public bool EnableCheckAll
        {
            get { return (bool)GetValue(EnableCheckAllProperty); }
            set { SetValue(EnableCheckAllProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EnableCheckAll.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EnableCheckAllProperty =
            DependencyProperty.Register("EnableCheckAll", typeof(bool), typeof(ExDataGrid), new PropertyMetadata(true));
        #endregion

        #region MaxCheckedItems
        public int MaxCheckedItems
        {
            get { return (int)GetValue(MaxCheckedItemsProperty); }
            set { SetValue(MaxCheckedItemsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxCheckedItems.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxCheckedItemsProperty =
            DependencyProperty.Register("MaxCheckedItems", typeof(int), typeof(ExDataGrid), new PropertyMetadata(0));
        #endregion

        #region Public properties
        public List<ExCommandPanel> CommandPanels { get; set; }
        public List<ExDropLocation> DropLocations { get; set; }
        public ExScrollBarBlock ScrollBar { get; set; }
        public Popup FilterMenuPopup { get; set; }
        public ExFilterMenu ExFilterMenu { get; set; }
        public Action<DataRow> OnChecked { get; set; }
        public DataRow TotalRow { get; set; }
        #endregion

        /// <summary>
        /// Set datagrid's itemsSource in order to fill datatable and  databind.
        /// </summary>
        public new IEnumerable ItemsSource
        {
            get
            {
                return base.ItemsSource;
            }          
            set
            {
                if (value is ExDataTable)
                {
                    this.dataTable = value as ExDataTable;
                }
                else
                {
                    this.dataTable = new ExDataTable();
                    this.dataTable.Fill(value);
                }

                this.dataTable.InitCheckState();

                this.RowEventBind();
                this.FillOrder();
                this.DataBind();

                this.SetCheckAll();
                this.SetScrollBar();
                this.SetRowNumbers(1);
            }
        }
     
        public ExDataGrid()
            : base()
        {
            
            this.DefaultStyleKey = typeof(ExDataGrid);
            this.AutoGenerateColumns = false;
            this.CommandPanels = new List<ExCommandPanel>();
            this.DropLocations = new List<ExDropLocation>();

            this.LayoutUpdated += (s, e) => this.RenderSummayRow();
            this.LoadingRow += (s, e) => InitDataGridCellClickEvent(e.Row);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.currentValuePanel = this.GetTemplateChild("currentValuePanel") as Border;
            this.currentValue = this.GetTemplateChild("currentValue") as TextBlock;
            
            this.rowNumbers = this.GetTemplateChild("rowNumbers") as ItemsControl;
            this.rowNumbers.Visibility = this.IsShowRowNumber ? Visibility.Visible : Visibility.Collapsed;
            this.SetRowNumbers(1);

            this.FilterMenuPopup = this.GetTemplateChild("filterMenuPopup") as Popup;
            this.ExFilterMenu = this.GetTemplateChild("exFilterMenu") as ExFilterMenu;

            this.BindFilterEvent();

            this.filterPanel = new ExFilterPanel();

            this.summaryRowPanel = this.GetTemplateChild("summaryRowPanel") as Panel;
            this.summaryRowViewer = this.GetTemplateChild("summaryRowViewer") as ScrollViewer;
            this.summaryRowViewer.Visibility = this.IsShowSummaryRow ? Visibility.Visible : Visibility.Collapsed;

            this.horizontalScrollbar = this.GetTemplateChild("HorizontalScrollbar") as ScrollBar;
            this.horizontalScrollbar.Scroll += (s, e) => this.summaryRowPanel.Margin = new Thickness(-e.NewValue, 0, 0, 0);
        }

        /// <summary>
        /// It enable to drag element outside into datagrid as column
        /// </summary>
        /// <param name="dragItems">dargItems</param>
        public void BindDragItems(IEnumerable<UIElement> dragItems)
        {
            foreach (UIElement element in dragItems)
            {
                if (element is ExDragItem)
                {
                    var dragItem = element as ExDragItem;
                    dragItem.OwnerExDataGrid = this;
                }
            }
        }

        /// <summary>
        /// Append the commandPanel(Sort,Order,Expand) to the columnHead of datagrid.
        /// </summary>
        /// <param name="cp">Command Panel</param>
        public void AddCommandPanel(ExCommandPanel cp)
        {
            this.CommandPanels.Add(cp);
            this.BindSortEvent(cp);
            this.BindDeleteEvent(cp);
            this.BindExpandEvent(cp);
        }

        /// <summary>
        /// Append the scrollBar to support pager.
        /// </summary>
        /// <param name="scrollBar">scrollBar</param>
        public void AddScrollBar(ExScrollBarBlock scrollBar)
        {
            if (!this.EnableVerticalScroll)
            {
                scrollBar.Enable(false);
                this.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
                return;
            }
            
            this.ScrollBar = scrollBar;

            scrollBar.OnValueChanging += value =>
            {
                int skipCount = Convert.ToInt32((currentDataRows.Count() - PageSize) * value);
                this.currentValue.Text = string.Format("Current from index: {0}", skipCount + 1);
                
                this.currentValuePanel.Visibility = Visibility.Visible;
            };
            
            scrollBar.OnValueChange += value =>
            {
                int skipCount = Convert.ToInt32((currentDataRows.Count() - PageSize) * value);
                this.SetDataSource(currentDataRows.Skip(skipCount).Take(PageSize));
                this.SetCheckAll();

                this.SetRowNumbers(skipCount + 1);

                this.currentValuePanel.Visibility = Visibility.Collapsed;
            };
        }

        /// <summary>
        /// Append a checkBox to support checking all.
        /// </summary>
        /// <param name="checkAll"></param>
        public void AddCheckAll(ExCheckBox checkAll)
        {                           
            this.checkAll = checkAll;

            this.checkAll.IsEnabled = this.EnableCheckAll;

            this.checkAll.Visibility = Visibility.Visible;

            this.checkAll.Click += (s, e) =>
            {
                isCheckAllTrigger = true;
                
                foreach (DataRow r in base.ItemsSource)
                {
                    r.IsChecked = (bool)this.checkAll.IsChecked;
                }

                isCheckAllTrigger = false;
            };
        }

        #region Private methods
        private void SetCheckAll()
        {
            if (this.checkAll == null)
                return;
            
            foreach (DataRow r in base.ItemsSource)
            {
                if (!r.IsChecked)
                {
                    this.checkAll.IsChecked = false;
                    return;
                }
            }

            this.checkAll.IsChecked = true;
        }

        private void SetScrollBar()
        {       
            if (this.ScrollBar == null)
            {
                this.Loaded += (s, e) => SetScrollBar();
                return;
            }

            this.ScrollBar.Init();
            
            if (currentDataRows.Count() <= PageSize)
            {
                ScrollBar.Enable(false);
                this.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
            }
            else
            {
                ScrollBar.Enable(true);
                this.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
                ScrollBar.SetViewportSize(Convert.ToDouble(PageSize) / (currentDataRows.Count() - PageSize));
            }
        }

        private void SetRowNumbers(int fromIndex)
        {
            if (!this.IsShowRowNumber || this.rowNumbers == null || this.currentDataRows == null)
                return;

            List<int> numbers = new List<int>();
            
            for (int i = fromIndex; i < Math.Min(this.currentDataRows.Count(), PageSize) + fromIndex; i++)
                numbers.Add(i);

            this.rowNumbers.ItemsSource = numbers;
        }

        private void DataBind()
        {
            if (string.IsNullOrEmpty(this.SortProperty))
                currentDataRows = this.dataTable;
            else
                currentDataRows = IsAscend ? this.dataTable.OrderBy(r => r[SortProperty]) : this.dataTable.OrderByDescending(r => r[SortProperty]);

            this.SetDataSource(currentDataRows.Take(PageSize));
            this.UpdateSummayRow();
        }

        private void SetDataSource(IEnumerable itemsSource)
        {
            if (string.IsNullOrEmpty(this.GroupProperty))
            {
                base.ItemsSource = itemsSource;
            }
            else
            {
                this.IsShowRowNumber = false;

                var collectionViewSource = new PagedCollectionView(itemsSource);
                var groupDescription = new ExPropertyGroupDescription(this.GroupProperty);
                collectionViewSource.GroupDescriptions.Add(groupDescription);
                base.ItemsSource = collectionViewSource;
            }
        }

        private void UpdateSummayRow()
        {
            if (!this.IsShowSummaryRow)
                return;

            this.summaryRow = new DataRow();

            foreach (var row in this.currentDataRows)
            {
                foreach (ExDataGridColumn column in this.Columns)
                {
                   if (column != null && column.FilterType == ExFilterType.Number)
                    {
                        string key = column.BindProperty;
                        if (!this.summaryRow.Data.ContainsKey(key))
                            this.summaryRow.Data.Add(key, 0);

                        this.summaryRow[key] = Convert.ToDouble(this.summaryRow[key]) +  Convert.ToDouble(row[key]);                  
                   }
                }
            }
        }

        private bool IsNotResize()
        {
            if (this.summaryRowPanel.Children.Count == 0 || this.Columns.Count > this.summaryRowPanel.Children.Count)
                return false;

            for (int i = 0; i < this.Columns.Count; i++)
            {
                var content = this.summaryRowPanel.Children[i] as FrameworkElement;
                var column = this.Columns[i] as ExDataGridColumn;
                if (content.Width != column.ActualWidth)
                    return false;

                if (content.Visibility != column.Visibility)
                    return false;
            }

            return true;
        }


        private void RenderSummayRow()
        {
            if (!this.IsShowSummaryRow || this.summaryRowPanel == null)
                return;

            if (this.TotalRow != null)
                this.summaryRow = this.TotalRow;
        
            if (object.Equals(this.summaryRowPanel.DataContext, this.summaryRow) && this.IsNotResize())
                return;

            this.summaryRowPanel.DataContext = this.summaryRow;
            this.summaryRowPanel.Children.Clear();
            
            foreach (ExDataGridColumn column in this.Columns)
            {
                if (column != null)
                {
                    var content = column.FilterType == ExFilterType.Number ? column.CellTemplate.LoadContent() as FrameworkElement : new Grid();
                     content.Width = column.ActualWidth;
                     content.Margin = new Thickness(0);
                     content.VerticalAlignment = VerticalAlignment.Center;
                     content.Visibility = column.Visibility;
                     this.summaryRowPanel.Children.Add(content);
                }
            }
        }
      
        private void RowEventBind()
        {
            this.dataTable.OnChecked += r =>
            {
                if (!isCheckAllTrigger)
                {
                    SetCheckAll();
                }
                
                if (this.OnChecked != null)
                    this.OnChecked(r);
            };
        }

        private void FillOrder()
        {
            foreach (ExDataGridColumn column in this.Columns)
                if (column != null && column.ShowOrderNumber)
                    this.dataTable.FillOrder(column.BindProperty);
        }

        private void InitDataGridCellClickEvent(DataGridRow row)
        {
            foreach (ExDataGridColumn column in this.Columns)
            {
                if (column != null)
                {
                    column.InitDataGridCellClickEvent(row);
                }
            }
        }
        #endregion

        private ExDataTable dataTable;
        private Panel summaryRowPanel;
        private ScrollViewer summaryRowViewer;
        private IEnumerable<DataRow> currentDataRows;
        private DataRow summaryRow;
        private ItemsControl rowNumbers;
        private ExFilterPanel filterPanel;
        private Border currentValuePanel;
        private TextBlock currentValue;
        private ExCheckBox checkAll;
        private bool isCheckAllTrigger;
        private ScrollBar horizontalScrollbar;
    }   
}
